package org.sfans.admin.event;

import java.util.Date;

import org.sfans.core.domain.Account;
import org.sfans.core.domain.AccountRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class FailedAuthHandler
		implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {

	private static final Logger LOG = LoggerFactory.getLogger(FailedAuthHandler.class);

	@Autowired
	private UserDetailsService userDetailsService;

	@Autowired
	private AccountRepository repository;

	@Value("${sfans.security.authentiation.account.maxBadCredentials:-1}")
	private int badCredentialsToLockAccount;

	@Override
	public void onApplicationEvent(final AuthenticationFailureBadCredentialsEvent event) {
		repository.findByUsername((String) event.getAuthentication().getPrincipal())
				.ifPresent(account -> updateAccount(account));
	}

	/**
	 * 更新登陆失败用户：内容是最后登陆失败日期和时间， 用户IP地址，增加失败次数等。
	 * 
	 * @param account
	 */
	private void updateAccount(final Account account) {
		LOG.info("Updating details of account:[{}] upon bad credentials", account.getUsername());

		account.setLastFailedAuth(new Date());
		account.setLastFailedAuthIP(currentRequestRemoteAddr());
		account.setFailedAuthCounter(account.getFailedAuthCounter() + 1);

		checkIfAccountLocked(account);

		repository.save(account);

	}

	/**
	 * 如果登陆失败的用户数量超过最大值，就锁定用户
	 * @param account
	 */
	private void checkIfAccountLocked(final Account account) {
		if (badCredentialsToLockAccount > 0 
				&& account.getFailedAuthCounter() 
					>= badCredentialsToLockAccount) {
			LOG.info("Locking account:[{}] due to {} invalid authentication attempts",
					account.getUsername(),account.getFailedAuthCounter());
			
			account.setLocked(true);
		}
	}

	private String currentRequestRemoteAddr() {
		return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
				.getRequest().getRemoteAddr();
	}

}
